szmc_data: 다섯번을 바닥부터 다시 짠 프로젝트
여러분은 비슷한 일을 하는 프로그램을 몇 번까지 새로 짜 보았나?
나는 식질머신을 위한 데이터를 관리하는 프로그램을 5번을 짜봤다.
5번째 츄라이는 지금도 진행 되고 있는 중이다.
5번을 짜자! 하고 짠 건 절대 아니고
최근에 돌아보니까 거의 비슷한 프로그램을 약 2년간 무려 5번을 바닥부터 새로 짰던 걸 알게 됬다.
절대로 의도한 건 아니고 걍 하다 보니 그렇게 되었다.
그야말로 삽질의 정수....
석사 쉑이면 연구를 해야 되는데 프밍 아키텍처 수련이나 하고 있었다
그래서 머신러닝 한다는 놈이 그 분야는 잘 모르고 프밍 패러다임 이야기나 하고 있고
생각없이 스킬 포인트 찍다보니 아는게 없어서 줫댔다 지금 나 땔감 그 자체임...
뭐 그건 그거고 모델 성능(정확도)딸 치다보면 어떻게든 되겠지 알게 모람
됐고 오늘은 5번을 새로 짰다는 프로그램 이야기를 해보자
szmc_data_old
원래 이름은 물론 szmc_data이다. 식질머신의 데이터를 관리하니까 붙인 이름이다. old는 나중에 붙였(던 것 같)다.
식질머신에서 사용하는 이미지와 데이터셋의 메타데이터를 관리한다.
사실 이 시점에서는 데이터를 체계적으로 관리한다는 생각이 없었던 것 같다.
그냥 데이터를 만들고 얼른 학습할 생각만 했던 거 같다. 앞뒤 안 보고 질러대기만 한 걸 보니...
- 규모: 165 커밋, 파이썬 LOC 2253.
- 기간: 2019년 3월 25일 ~ 2019년 5월 13일. 약 두 달.
구조
├── access_db.py DB에 접근하는 코드. class DB가 선언되어 있다. 문자열로 생sql을 쓴다..
├── create_db.py DB를 만드는 코드. DDL(create table 같은 거)이 문자열로 박혀 있다..
├── cut_methods.py 이미지를 자를 때 썼던 코드. cnet 학습 코드가 큰 이미지를 처리할 수 없어서 만듦.
테스트 코드가 제품 코드 아래에 같이 있다...
├── dataset 데이터셋이 저장되었던 폴더. "스크립트"라고 생각하여 데이터를 코드와 같은 곳에 두던 시절...
│ ├── clean_fmd_comics190415
│ ├── trainNH_validO_190414
│ └── uno_train_cleans190415
├── fp.py 함슬람용 함수 모음. 커리된 함수 앞에는 c를 붙여뒀다. 대부분 그냥 funcy 함수를 임포트했을 뿐임...
├── futils.py 파일 처리용 함수 모음. 함수가 딱 3개, 재귀적으로 경로 뽑는 거랑 닝겐 정렬, Path의 write_text 랲퍼.
├── get_ids.py 방대한 단부루 데이터에서 고화질(absurdres)과 단색(monochrome)을 뽑아내어 rsync하기 위한 스크립트
├── gui_lab.py 어노테이션을 위한 구이 작성을 위한 실험용 코드. 미친놈이 이걸 왜 프로젝트 안에 두냐...
2년전 나는 진짜 전설이다.. 요새는 밖으로 뺀다 이런거. 헤더 주석에 Delete it! 적혀 있는데 삭제 안함 ㅋㅋㅋㅋ
├── gui.py 어노테이션을 위한 구이. QT로 짰다. 어노테이션은 KKR이 도움을 많이 줫었다.
요새는 Via(vgg img annotator)로 날먹한다...
├── image_cutter.py 이미지를 자를 때 실제로 썼던 코드. cut_methods의 함수를 호출한다. 지금 보니까 쓰레기 같다 ㅋㅋㅋ
이걸 짤 때 작성한 블로그 글이 있다. https://blog.naver.com/rhdnfka94/221514214220
cut_methods를 짤 때 쓴 것도 있다: https://blog.naver.com/rhdnfka94/221513369159
├── imgcut4paper.py KCC2019 논문을 위해 이미지를 잘랐나 보다. 주석도 없고 진짜 스크립트라서 무슨 용도인지 잘 모르겠다.
├── imutils.py utils라고 되있지만 유틸은 무슨 이 프로젝트에서만 쓰는 코드를 유틸리티라고 네이밍해뒀다...
이미지 관련 작업을 위한 함수들이 있다.
├── insert_rows.py 디비에 데이터를 넣을 때 쓴 스크립트다. 이걸로 넣어 놓고 구이로 어노테이션 했다.
├── json2flist.py cnet 학습에는 특별한 파일인 flist가 필요했었다. 그걸 만드는 스크립트.
├── make_flist_tmp.py 이걸로 cnet 학습용 flist를 만들었었는데, 이미지가 너무 크면 학습이 안 되는 상황이 발생했었다.
그래서 tmp를 붙였나보다. 이것도 삭제해야 되는건데 삭제를 안 한 코드 같다.
├── make_id_dset.py ...뭐하는 코드인지 모르겠다. 헤더 주석을 봐도 모르겠다. 깨알같은 오타 Renmae..
Change origin dataset directory to id dataset directory (NOTE: Renmae files)
├── merge_db.py 디비 두개를 하나로 퓨전! KKR과 내가 따로 작업한 디비 두개를 하나로 만들 때 썼다.
├── merge_jsons2flist.py 하나이상의 json을 flist로 합친다. json은 이미지 경로에 메타데이터가 맵핑된 dict 하나다.
├── mk_snet_dset.py ...뭐하는 코든지 잘 모르겠는데 파이슨 generator 한번 쓰면 사라지는 거에 뚝배기 깨진 기억은 난다 ㅅㅂ ㅋㅋㅋ
그거 뚝배기 깨지고 나서 fp.py 유지보수하는거 현타 찐하게 왔던 기억이 있다.
피처는 안 만들고 무슨 함수람에 박고 있네 시발 거.. 그런 생각을 했었다.
몬가... 몬가 데이터 분석을 해서 엑셀 파일로 만든 거 같다. 논문을 위해서 데이터의 난이도를 측정했던가...
├── ready4snet_dset.py 헤더 주석을 보니까 snet 데이터셋을 위한 Santy 체크를 한다고 한다. 샨티 샨티 카레카레야~ 완전 조아 아 레알 조아
대체 헤더 주석에 오타가 왤케 처 많냐... 뚝배기를 까버려야 된다 진짜
이 때 생각해보면 다같이 어노테이션 했던 snet 마스크에 오류가 많았던 기억이 난다. 그걸 많이 고친 듯.
├── scripts 뭐지? 이미 전부 스크립트인데 무슨 또 스크립트지? 무엇을 암시하는 것이지?
│ └── create_imgs_json.py 헤더 주석이 없어서 뭐하는건지 모르겠다. 단부루 데이터를 뭔가 북짝북짝하는 거 같다.
├── snet_data
│ └── look_and_feel_check.py 말 그대로 데이터의 이미지를 직접 띄워보고 확인하는 코드.
├── threshold_img.py snet 마스크는 0/255 말고 다른 값이 있었다! 대 단 하 다! 왜 그랬을까...
왜 이 때는 png로 1비트 마스크로 저장하지 않았는지 모르겠다. 아무튼 그걸 고치려고 이미지에 쓰레시홀드를 걸었다.
└── utils.py 텅 비어 있다. 아니 어이가 없어서 웃음밖에 안 나옴 ㅋㅋㅋㅋㅋ
구조라할 만한게 없고 걍 모든 것이 root에 존재한다.
이 때는 파이썬에서 프로젝트를 어떻게 구성하는지 몰랐다...
파이썬은 걍 스크립트 언어고 스크립트로 쓰는게 맞다고 자위질하고 있던 시절
요소 분석
적용 패러다임: 스크립팅, 함수형 프로그래밍(fp.py).
함수형 프밍 뽕맞던 시기인데 funcy를 쓰면서 커링된 함수 이름에 c를 붙여 놓고 있다. 최근 fp랑은 많이 다름.
데이터 저장: sqlite 디비, json 일부.
데이터 관리: 파이썬 내장 수꼴라이트 라이브러리에 문자열로 생sql을 박고 있음
DB 클래스를 선언해서 상태를 관리하면서 다양한 메서드를 노출시켜 놓음.
이 클래스 이후로는 라이브러리 사용을 위해서가 아니라면 클래스를 만든 적이 없는 듯.
몇몇 데이터는 json으로 저장했다. 흠..... 왜? 디비에 넣을 생각을 하지 않았던 거 같다.
유저 인터페이스: sys.argv를 이용한 간단한 cli를 파일마다 만들어 박았다.
아니면 그냥 인터페이스가 없다 한번 쓰고 버릴 스크립트라서...
어노테이터는 QT 구이를 썼다.
테스트: 없거나 제품 코드 바로 밑에 작성됨. TDD를 적용.
스크립팅으로 해결한 코드는 그냥 print로 결과를 확인하면 서 함. 그 정도만 해도 잘만 썼다..
프로젝트 관리: git을 쓰긴 썼다. 마스터에 다 때려박았지만. README는 작성함.
이슈도 풀리퀘도 없다. 깃 플로우? 아예 있는지도 몰랐던 거 같다.
사실 혼자서 스크립트 짜는데 누가 그딴 짓을 하겠는가?
리드미는 그저 빛 KKR이 어노테이션하는 데 도움되는 것들을 써 두었다. 진짜 내가 다음에 맛있는거 사줄게
이걸로 한 일: 식질머신 v0 버전을 만들 때 필요했던 학습용 데이터를 만들었다.
snet과 cnet을 위한 데이터를 이걸로 다 만들었다.
단부루 데이터를 수집하고 어노테이션 했다.
작성 코드 예시:
# 함수형 파이프라인
jsons2id_ext_seq = rcompose(
partial(mapcat, jsonpath2lines),
partial(map, json.loads),
partial(filter, has_tag('absurdres')),
partial(filter, has_tag('monochrome')), # and
partial(map, lambda j: (int(j['id']), j['file_ext']) )
)
ㅋㅋ funcy에서 추천하는 방식인데 나중 버전에서는 rcompose를 랩핑한 fp.go로 짠다.
# 디비 관련
def get_data_where(self,where):
table = 'data'
cur = self.db.cursor()
cur.execute(
'''SELECT *
FROM data
WHERE {}
ORDER BY CAST(id AS INT)'''.format(where))
col_names = self.get_column_names(table)
return pd.DataFrame(cur.fetchall(), columns=col_names)
db.execute('''
CREATE TABLE IF NOT EXISTS image(
id TEXT PRIMARY KEY NOT NULL,
file_path TEXT NOT NULL,
extension TEXT CHECK( extension IN ('png','jpg','gif') ) NOT NULL,
file_size INTEGER CHECK( file_size > 0 ) NOT NULL,
height INTEGER CHECK( height > 0 ) NOT NULL,
width INTEGER CHECK( width > 0 ) NOT NULL,
FOREIGN KEY(id) REFERENCES data(id)
);
''')
우욱
맺음말
처음으로 만든 데이터 관리 프로젝트다.
아니 프로젝트라기보다는 스크립트 덩어리에 가깝다.
여러모로 줫같고 미숙한 코드지만 제 할일을 다 했던 코드다.
스크립트처럼 써서, 한번 쓰고 버려졌지만 snet/cnet을 위한 데이터는 잘 만들어 냈다.
이걸로 만든 데이터로 결과적으로 학습도 했고, 논문도 썼고, 식질머신 v0 버전도 만들었다.
제일 거지 같은 코드지만 반대로 가장 생산성이 높았다(최근 버전 전까지는 말이야..)
이 코드는 왜 버려졌을까? 보통 코드를 짠 당시에는 다 좋아 보이는데도 버려진 이유는?
사실 이런 프로젝트는 할 일 다 보고 나서 딴 거 하다가 다시 돌아와서 볼 때 버려진다.
보통 과거의 자신이 얼마나 줫같은 코드를 짰는지 보고 경악하다가 새로 짜기에 이른다.
아마도 버려진 이유는...
- 스크립트라서 짜다 보니까 어디에 뭐가 박혀있는지 파악이 안 되기 시작했다.
- 디비 스키마가 다양한 데이터를 담기에는 부족했다(고 생각한 거 같다)
- 식질머신 v0를 짠 후 코드를 프로젝트로 구조화하는 것을 신경쓰기 시작했다(1.과 같은 이유)
- 전부 스크립트라서 코드 재활용 따위는 없었다. 필요하면 새로 짜야 했다.
이것들 다 유추하고 기억해낸 건데, 다음에는 코드가 버려지는 이유를 꼭 써둬야겠다.
분명한 이유를 가지고 기껏 잘 만든 코드를 버렸는데... 이유가 기억이 잘 안 난다니..